package org.swift.mashup.engine.model;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.swift.commons.client.MeasuredInputStream;
import org.swift.mashup.engine.concurrency.agenda.Action;
import org.swift.mashup.engine.concurrency.agenda.ActionException;
import org.swift.mashup.engine.model.processors.FromProcessor;
import org.swift.mashup.engine.model.processors.FromProcessorFactory;

@SuppressWarnings("unchecked")
public class Grab implements Action {
	private static final Log log = LogFactory.getLog(Grab.class);
	
	private static final String REST_KEY = "{restparam}";
	private static final String HEADER_KEY = "{headerparam}";

	private final String name;
	private final From from;
	private final Activity activity;
	private final FromProcessor processor;
	private final Map<String, Object> parameters = new HashMap<String, Object>();
	private final Map<String, Object> sendParameters = new HashMap<String,Object>();
	private final Map<String, Object> restParameters = new HashMap<String,Object>();
	private final Map<String, String> headerParameters = new HashMap<String,String>();
	private String encoding;
	private final String mashupName;
	
	private ActivityResultComponent output;
	
	public Grab(String name, From from,
			Activity activity, Map<String, ? extends Object> withMap) {
		this.name = name;
		this.from = from;
		this.activity = activity;
		if (withMap != null) {
			this.parameters.putAll(withMap);
		}
		this.processor = FromProcessorFactory.getFromProcessor(from.getFromProcessor());
		this.mashupName = "";
	}

	public Grab(String name, From from,
			Activity activity, Map<String, ? extends Object> withMap,
			Map<String, ? extends Object> sendParameters, String encoding) {
		this.name = name;
		this.from = from;
		this.activity = activity;
		if (withMap != null) {
			this.parameters.putAll(withMap);
		}
		if (sendParameters != null){
			this.processParameterMap((Map<String, Object>) sendParameters);
		}
		this.processor = FromProcessorFactory.getFromProcessor(from.getFromProcessor());
		this.encoding = encoding;
		this.mashupName = "";
	}

	public Grab(String name, From from,
			Activity activity, Map<String, ? extends Object> withMap,
			Map<String, ? extends Object> sendParameters,
			Map<String, ? extends Object> restParameters, String encoding) {
		this.name = name;
		this.from = from;
		this.activity = activity;
		if (withMap != null) {
			this.parameters.putAll(withMap);
		}
		if (sendParameters != null){
			this.sendParameters.putAll(sendParameters);
		}
		if (restParameters != null){
			this.restParameters.putAll(restParameters);
		}
		this.processor = FromProcessorFactory.getFromProcessor(from.getFromProcessor());
		this.encoding = encoding;
		this.mashupName = "";
	}

	public Grab(String name, From from,
			Activity activity, Map<String, ? extends Object> withMap,
			Map<String, ? extends Object> sendParameters, String encoding, String mashupName) {
		this.name = name;
		this.from = from;
		this.activity = activity;
		if (withMap != null) {
			this.parameters.putAll(withMap);
		}
		if (sendParameters != null){
			this.processParameterMap((Map<String, Object>) sendParameters);
		}
		this.processor = FromProcessorFactory.getFromProcessor(from.getFromProcessor());
		this.encoding = encoding;
		this.mashupName = mashupName;
	}

	public void run() {
		try {
			final Object fromValue = this.from.getValue(this.sendParameters, this.restParameters, this.headerParameters);
			ActivityContext context = createActivityContext(fromValue);	
			context = this.processor.processData(context);
			this.output = this.activity.doActivity(context, this.parameters);		
		} catch (Exception e) {
			this.output = new ActivityResultItem(new ActionException(name, e.getMessage(), e));
		}
	}
	
	protected ActivityContext createActivityContext(Object fromValue) throws NotSupportedActivityException {		
		if ( fromValue instanceof MeasuredInputStream ){  
			MeasuredInputStream stream = (MeasuredInputStream)fromValue;
			return new InputStreamActivityContext(stream, stream.size());	
	    } else if(fromValue instanceof InputStream) {
			return new InputStreamActivityContext((InputStream)fromValue, -1);				
		} else if (fromValue instanceof String) {
			return new ByteArrayActivityContext((String)fromValue);
		} else if (fromValue instanceof byte[]) {
			return new ByteArrayActivityContext((byte[])fromValue);
		}
		throw new NotSupportedActivityException("Could not create an ActivityContext from " + fromValue.getClass().toString());
	}

	public String getName() {
		return name;
	}

	public ActionOutput getActionResult() {
		return output;
	}
	
	@Override
	public String toString() {
		return "Grab Action [name=\"" + name + "\", from=\"" + from + "\"]";
	}
	
	public String getEncoding() {
		return encoding;
	}
	
	public void processParameterMap(Map<String, Object> map){
		if(!map.isEmpty()){
			Set<Map.Entry<String, Object>> entries = map.entrySet();
			for(Map.Entry<String, Object> entry: entries){
				if(entry.getKey().startsWith(REST_KEY)){
					this.restParameters.put(entry.getKey(), entry.getValue());
				}else if(entry.getKey().startsWith(HEADER_KEY)){
					this.headerParameters.put(entry.getKey().substring(HEADER_KEY.length()), entry.getValue().toString());
				}else{
					this.sendParameters.put(entry.getKey(), entry.getValue());
				}
			}
		}
	}
	
	public String getMashupName() {
		return mashupName;
	}
}
